package im.demo.fileserv;

import com.google.gson.Gson;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
  
@WebServlet(name = "FileUploadServlet", urlPatterns = {"/upfile"})
@MultipartConfig(fileSizeThreshold=1024*1024*10,    // 10 MB 
                 maxFileSize=1024*1024*50,          // 50 MB
                 maxRequestSize=1024*1024*100)      // 100 MB
public class FileUploadServlet extends HttpServlet {

    private Gson gson = new Gson();

    private static String uptoken = DigestUtils.sha1Hex(".fileserve"); //3662b0f349d9a059c181e8352223f3bb8912c2b4

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.setAttribute("uptoken", uptoken);
        req.setAttribute("contextPath", req.getContextPath());

        req.getRequestDispatcher("/fileupfrom.ftl").forward(req, resp);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out =  response.getWriter();

        // 以 json 的方式返回
        response.setContentType("application/json;charset=UTF-8");

        Part tokenPart = request.getPart("uptoken");

        if ( tokenPart != null ) {

            if ( !IOUtils.toString(tokenPart.getInputStream()).equals(uptoken) ) {

                response.setStatus(403);

                Map<String, String> err = new HashMap<>();

                err.put("errMsg", "token invalid");
                err.put("errCode", "403");

                out.print(gson.toJson(err));

                return;

            }

        } else {

            response.setStatus(402);

            Map<String, String> err = new HashMap<>();

            err.put("errMsg", "token not specify");
            err.put("errCode", "402");

            out.print(gson.toJson(err));

            return;

        }

        // 当前 application 路径
        String applicationPath = request.getServletContext().getRealPath("");

        // 文件 Servlet ContextPath

        String fileServletContentPath = ((Properties)request.getServletContext().getAttribute("fileProps")).getProperty("fileserver.contextpath");

        // 文件存储目录
        String fileStorePath = resolveFileStorePath(applicationPath, fileServletContentPath);

        String fileName = null;

        Part filePart = request.getPart("file");

        if ( filePart != null ) {

            fileName = getFileName(filePart);

            if ( FilenameUtils.getExtension(fileName).matches("exe|php|jsp|class|java") ) {

                response.setStatus(405);

                Map<String, String> err = new HashMap<>();

                err.put("errMsg", "您上传的文件类型是不被允许的，服务器拒绝了您此次的请求");
                err.put("errCode", "405");

                out.print(gson.toJson(err));

                return;

            }

            String storeFileName = generateFileKey(fileName) + "." + FilenameUtils.getExtension(fileName);

            // 写入数据库
            DB.addFile(storeFileName, fileName);

            filePart.write(FilenameUtils.concat(fileStorePath, storeFileName));

            response.setStatus(201);

            Map<String, String> success = new HashMap<>();

            success.put("key", storeFileName);

            success.put("originName", fileName);

            out.print(gson.toJson(success));


        } else {

            response.setStatus(401);

            Map<String, String> err = new HashMap<>();

            err.put("errMsg", "没有指定文件");
            err.put("errCode", "401");

            out.print(gson.toJson(err));

        }

    }
  
    /**
     * 从 HTTP header content-disposition 中获取文件原有名称
     */
    private static String getFileName(Part part) throws UnsupportedEncodingException {
        String contentDisp = part.getHeader("content-disposition");
        String[] tokens = contentDisp.split(";");
        for (String token : tokens) {
            if (token.trim().startsWith("filename")) {
                return new String(token.substring(token.indexOf("=") + 2, token.length()-1).getBytes("ISO-8859-1"), Charset.forName("UTF-8"));
            }
        }
        return "";
    }

    private static String generateFileKey(String fileName) {

        // 文件名和当前时间的 sha1 值

        Integer now = LocalDateTime.now().getSecond();

        return DigestUtils.sha1Hex( fileName + now );

    }

    private static String resolveFileStorePath(String appPath, String fileServletContentPath) {

        // 取父路径，来到 tomcat webapps 目录下

        return new File(Paths.get(appPath).getParent().toAbsolutePath().toString(), fileServletContentPath).toString();

    }
}